﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects;
using System.Text.RegularExpressions;

namespace EZMemoLib
{
    public class VoicePart
    {
        public string FilePath { get; set; }
        public string FileMark { get; set; }
        public bool IsPart { get; set; }
        public TimeSpan PartStart { get; set; }
        public TimeSpan PartEnd { get; set; }
    }

    public class PlayFlow
    {
        public string FlowName { get; set; }
        public string FlowMark { get; set; }
    }

    public class Player
    {
        EZMemoEntities database = new EZMemoEntities();

        MemoLib.MemoBook memoBook = null;

        static Player _Player = null;
        public static Player Instance(string loginID, long bookID)
        {
            if (_Player == null)
            {
                _Player = new Player(loginID, bookID);
                _Player.OnNewEnd += new EventHandler(_Player_OnNewEnd);
                _Player.OnRepetitionEnd += new EventHandler(_Player_OnRepetitionEnd);
                _Player.OnDrillEnd += new EventHandler(_Player_OnDrillEnd);
            }
            else
            {
                _Player.LoginID = loginID;
                _Player.BookID = bookID;
            }

            return _Player;
        }

        static void _Player_OnDrillEnd(object sender, EventArgs e)
        {

        }

        static void _Player_OnRepetitionEnd(object sender, EventArgs e)
        {

        }

        static void _Player_OnNewEnd(object sender, EventArgs e)
        {

        }

        public static TimeSpan ParseTimeSpan(string timeString)
        {
            TimeSpan ts;
            TimeSpan.TryParse(timeString, out ts);
            return ts;
        }

        public static List<PlayFlow> GetPlayFlows()
        {
            List<PlayFlow> list = new List<PlayFlow>();

            list.Add(new PlayFlow { FlowName = "Default", FlowMark = "" });
            list.Add(new PlayFlow { FlowName = "S=QADP", FlowMark = "S=QADP" });
            list.Add(new PlayFlow { FlowName = "Q=SRDA", FlowMark = "Q=SRDA" });
            list.Add(new PlayFlow { FlowName = "A=SQDP", FlowMark = "A=SQDP" });
            list.Add(new PlayFlow { FlowName = "QS=AS", FlowMark = "QS=AS" });

            return list;
        }


        public static VoicePart ParseVoicePart(string voicePartString)
        {
            //[00:08.00][00:08.00][B]
            //[00:08.00][00:08.00][L]

            //[B] books sound path
            //[L] lesson sound path
            //[DOA] this app sound folder
            //[DOB] books sound folder []
            //[DOC] customer books sound folder
            //[NOL] name of the lesson
            //[C] customer lesson sound path
            Match m;
            Regex r =
             new Regex(@"\[(?<1>[^\]]+)\]", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);

            int index = 0;
            string startTime = string.Empty;
            string endTime = string.Empty;
            string pathMark = string.Empty;

            for (m = r.Match(voicePartString); m.Success; m = m.NextMatch())
            {
                switch (index)
                {
                    case 0:
                        startTime = m.Groups[0].Value.Trim(new char[] { '[', ']' });
                        var arr = startTime.Split(':');
                        if (arr != null && arr.Length == 2)
                        {
                            startTime = string.Format("0:{0}", startTime);
                        }
                        break;
                    case 1:
                        endTime = m.Groups[0].Value.Trim(new char[] { '[', ']' });
                        var arr2 = endTime.Split(':');
                        if (arr2 != null && arr2.Length == 2)
                        {
                            endTime = string.Format("0:{0}", endTime);
                        }

                        break;
                    case 2:
                        pathMark = m.Groups[0].Value.Trim(new char[] { '[', ']' });
                        break;

                    default:
                        break;
                }
                index++;
            }

            switch (pathMark)
            {
                case "[L]":

                    break;
                default:
                    break;
            }

            VoicePart part = new VoicePart
            {
                FileMark = pathMark,
                PartStart = ParseTimeSpan(startTime),
                PartEnd = ParseTimeSpan(endTime)
            };

            return part;
        }

        public static VoicePart ParseVoicePartLrc(string lrcLineString, string lrcLineString2)
        {
            //[al:新概念英语（二）]
            //[ar:MP3 同步字幕版（美音）]
            //[ti:In or Out?]
            //[by:更多学习内容，请到VeryCD.com]
            //[00:11.22]Why did Rex run away?
            //[00:15.52]Our dog, Rex, used to sit outside our front gate and bark.
            //[00:22.54]Every time he wanted to come into the garden he would bark until someone opened the gate.
            //[00:30.39]As the neighbours complained of the noise, my husband spent weeks training him to press his paw on the latch to let himself in.
            //[00:41.56]Rex soon became an expert at opening the gate.
            //[00:46.22]However, when I was going out shopping last week, I noticed him in the garden near the gate.
            //[00:54.81]This time he was barking so that someone would let him out!
            //[01:00.09]Since then, he has developed another bad habit.
            //[01:05.10]As soon as he opens the gate from the outside, he comes into the garden and waits until the gate shuts.
            //[01:14.07]Then he sits and barks until someone lets him out.
            //[01:18.58]After this he immediately lets himself in and begins barking again.
            //[01:26.28]Yesterday my husband removed the gate and Rex got so annoyed we have not seen him since.

            Match m;
            Regex r =
             new Regex(@"\[(?<1>[^\]]+)\]", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);

            int index = 0;
            string startTime = string.Empty;
            string endTime = string.Empty;
            string pathMark = string.Empty;
            string text = string.Empty;

            string lrc = lrcLineString + "" + lrcLineString2;

            for (m = r.Match(lrc); m.Success; m = m.NextMatch())
            {
                switch (index)
                {
                    case 0:
                        startTime = m.Groups[0].Value.Trim(new char[] { '[', ']' });
                        var arr = startTime.Split(':');
                        if (arr != null && arr.Length == 2)
                        {
                            startTime = string.Format("0:{0}", startTime);
                        }
                        break;
                    case 1:
                        endTime = m.Groups[0].Value.Trim(new char[] { '[', ']' });
                        var arr2 = endTime.Split(':');
                        if (arr2 != null && arr2.Length == 2)
                        {
                            endTime = string.Format("0:{0}", endTime);
                        }

                        break;
                    case 2:
                        pathMark = m.Groups[0].Value.Trim(new char[] { '[', ']' });
                        break;

                    default:
                        break;
                }
                index++;
            }

            if (!string.IsNullOrEmpty(startTime))
            {
                pathMark = lrcLineString.Substring(startTime.Length);
            }

            var tryStartTime = ParseTimeSpan(startTime);
            VoicePart part = null;
            if (tryStartTime.TotalMilliseconds > 0)
            {
                part = new VoicePart
                {
                    FileMark = pathMark,
                    PartStart = ParseTimeSpan(startTime),
                    PartEnd = ParseTimeSpan(endTime),
                };
            }

            return part;
        }


        public static string BuildVoicePart(VoicePart part)
        {
            string voicePartString = string.Format("[{0}][{1}][{2}]", part.PartStart.ToString("g"), part.PartEnd.ToString("g"), part.FileMark);
            //[00:08.00][00:08.00][B]
            //[B] books sound path
            //[L] lesson sound path
            //[DOA] this app sound folder
            //[DOB] books sound folder []
            //[DOC] customer books sound folder
            //[NOL] name of the lesson
            //[C] [DOB] lesson sound path
            return voicePartString;
        }

        public VoicePart GetVoicePartFromItem(MemoItem item)
        {
            VoicePart part = null;
            if (!string.IsNullOrEmpty(item.ItemSound))
            {
                part = Player.ParseVoicePart(item.ItemSound);
            }
            else
            {
                part = new VoicePart { FileMark = "", PartStart = new TimeSpan(), PartEnd = new TimeSpan() };
            }

            if (part != null)
            {
                switch (part.FileMark)
                {
                    case "B":
                        break;

                    case "L":
                    default:
                        if (item.Category != null)
                        {
                            var lesson = BookBusiness.Instance().GetCategory(item.Category.Value);

                            if (lesson != null && item.Book != null)
                            {
                                part.FilePath = Context.FilePathLesson(item.Book.Value, lesson.CategoryNumber);

                            }
                        }
                        break;
                }

            }
            return part;

        }

        public Player(string loginID, long bookID)
        {
            this.LoginID = loginID;

            this.BookID = bookID;

            this.memoBook = MemoLib.MemoBook.Instance((int)bookID);
        }

        string _LoginID;
        public string LoginID
        {
            get
            {
                return _LoginID;
            }
            set
            {
                _LoginID = value;
            }
        }

        long _BookID;
        public long BookID
        {
            get
            {
                return _BookID;
            }
            set
            {
                _BookID = value;
            }
        }

        public ObjectSet<MemoToday> GetList()
        {

            ObjectSet<MemoToday> list = database.MemoToday;
            foreach (var item in list)
            {

            }

            return list;
        }

        public void SaveResult(long itemID, CheckResultType result)
        {
            ItemType itype = ItemType.Repetition;
            //int itemID = _Item
            MemoHistory history = database.MemoHistory.Where(c => c.ItemID == itemID && c.LoginID == this.LoginID).FirstOrDefault();
            bool isExistHistory = false;
            if (history == null)
            {
                itype = ItemType.New;
                history = new MemoHistory { ItemID = itemID, Book = this.BookID, IsActivted = true, LoginID = this.LoginID };
            }
            else
            {
                isExistHistory = true;
            }

            //if (history.ResultA == null) history.ResultA = 0;
            //if (history.ResultB == null) history.ResultB = 0;
            //if (history.ResultC == null) history.ResultC = 0;
            //if (history.ResultD == null) history.ResultD = 0;
            //if (history.CurveIndex == null) history.CurveIndex = 0;
            if (history.StartDate == null) history.StartDate = DateTime.Now;

            switch (result)
            {
                case CheckResultType.DidnotKnow:
                    history.ResultA++;
                    history.CurveIndex++;
                    history.LastResult = -1;
                    history.LastDate = DateTime.Now;

                    //if don't konw, then diff + 1, then drill + 1
                    history.Difficulty++;

                    this.DrillSave(new MemoToday { BoolkID = this._BookID, ItemID = itemID, TodayDate = DateTime.Now, LoginID = this.LoginID, CheckResult = -1 });

                    break;
                case CheckResultType.Knew:
                    history.ResultC++;
                    history.CurveIndex++;

                    history.LastResult = 1;
                    history.LastDate = DateTime.Now;

                    this.DrillClear (itemID);

                    break;
                case CheckResultType.Almost:
                default:
                    //if almost , then diff not change , then drill + 1
                    this.DrillSave(new MemoToday { BoolkID = this._BookID, ItemID = itemID, TodayDate = DateTime.Now, LoginID = this.LoginID, CheckResult = -1 });
                    history.ResultB++;
                    break;
            }

            if (isExistHistory)
            {

            }
            else
            {
                database.AddToMemoHistory(history);
            }
            int rc = database.SaveChanges();

        }

        public void DrillSave(MemoToday drill)
        {
            var oldDrill = database.MemoToday.Where(c => c.ItemID == drill.ItemID && c.LoginID == this.LoginID).FirstOrDefault();
            if (oldDrill == null)
            {
                drill = new MemoToday { ItemID = drill.ItemID, BoolkID = drill.BoolkID, LoginID = drill.LoginID, TodayDate = DateTime.Now };
                database.AddToMemoToday(drill);
                int rc = database.SaveChanges();

                if (rc > 0)
                {
                    //add dirll to list
                    _DrillList.Add(drill);
                    _DrillToday = _DrillList.Count;
                }

            }
            else
            {
                //if it's exist, igore it
            }
        }

        /// <summary>
        /// Clear drills before
        /// </summary>
        public List<MemoToday> GetDrillListFromDB()
        {
            DateTime dt = DateTime.Now.Date;
            var drills = database.MemoToday.Where(c => c.TodayDate >= dt && c.BoolkID == this.BookID && c.LoginID == this.LoginID);
            if (drills != null)
            {
                return drills.ToList();
            }
            return null;

        }

        /// <summary>
        /// Clear drills before
        /// </summary>
        public void DrillClear()
        {
            DateTime dt = DateTime.Now.Date;
            var oldDrills = database.MemoToday.Where(c => c.TodayDate < dt && c.LoginID == this.LoginID);

            if (oldDrills != null)
            {
                foreach (var old in oldDrills)
                {
                    database.MemoToday.DeleteObject(old);


                }

                int rc = database.SaveChanges();
                if (rc > 0)
                {
                    _DrillCursor = 0;
                    //remove dirll to lis
                    _DrillList.Clear ();//.Remove(old);
                    _DrillToday = _DrillList.Count;
                }
            }

        }

        /// <summary>
        /// Clear drill when it was passed
        /// </summary>
        public void DrillClear(long itemID)
        {
            var drill = database.MemoToday.Where(c => c.ItemID == itemID && c.LoginID == this.LoginID).FirstOrDefault();

            if (drill != null)
            {
                database.MemoToday.DeleteObject(drill);
                int rc = database.SaveChanges();

                if (rc > 0)
                {
                    _DrillCursor--;

                    //remove dirll to lis
                    if (_DrillCursor >= 0 && _DrillCursor < _DrillList.Count)
                    {
                        _DrillList.RemoveAt(_DrillCursor);
                        _DrillToday = _DrillList.Count;

                        //Dirll Curso donn't need to move
                        //_DrillCursor 
                        if (_DrillCursor > _DrillList.Count)
                        {
                        }
                    }
                    else
                    {
                        _DrillCursor = 0;
                    }
                }
            }


        }

        public void SaveItem(MemoItem item)
        {
            if (item == null)
                return;

            //int itemID = _Item
            var list = database.MemoItem.Where(c => c.ItemID == item.ItemID);

            MemoItem ri = null;
            if (list != null && list.Count() > 0)
            {
                ri = list.First();
            }
            if (ri == null)
            {
                database.AddToMemoItem(item);
            }
            else
            {
                ri.ItemID = item.ItemID;
                ri.Category = item.Category;
                ri.Book = item.Book;
                ri.ItemAnswer = item.ItemAnswer;
                ri.ItemDesc = item.ItemDesc;
                ri.ItemStatus = item.ItemStatus;
                ri.ItemFlow = item.ItemFlow;

                ri.ItemName = item.ItemName;
                ri.ItemOrder = item.ItemOrder;
                ri.ItemPicture = item.ItemPicture;
                ri.ItemQuestion = item.ItemQuestion;
                ri.ItemRelation = item.ItemRelation;
                ri.ItemSound = item.ItemSound;
                ri.ItemSource = item.ItemSource;
                ri.ItemType = item.ItemType;
                ri.ItemVideo = item.ItemVideo;
            }
            int rc = database.SaveChanges();

        }



        List<MemoToday> _RepeList = new List<MemoToday>();
        List<MemoToday> _NewList = new List<MemoToday>();
        List<MemoToday> _DrillList = new List<MemoToday>();

        //find stat info in history object
        List<MemoHistory> _HistoryList = new List<MemoHistory>();

        int _RepeCursor = 0;
        public int RepeCursor { get { return _RepeCursor; } }
        int _NewCursor = 0;
        public int NewCursor { get { return _NewCursor; } }
        int _DrillCursor = 0;
        public int DrillCursor { get { return _DrillCursor; } }

        public int NewItemMax { get; set; }

        //private long _Total = 0;
        public long TotalCursor { get { return _RepeCursor + _NewCursor; } }
        public long Total { get { return _RepetitionToday + _NewToday; } }

        private long _RepetitionToday = 0;
        public long RepetitionToday { get { return _RepetitionToday; } }

        private long _DrillToday = 0;
        public long DrillToday { get { return _DrillToday; } }

        private long _NewToday = 0;
        public long NewToday { get { return _NewToday; } }

        //public int Today { get { return _TodayList.Count; } }


        public event EventHandler OnRepetitionEnd;
        public event EventHandler OnNewEnd;
        public event EventHandler OnDrillEnd;
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public MemoItem GetNextItem()
        {
            MemoItem mi = GetNextRepetition();

            if (mi == null)
            {
                if (OnRepetitionEnd != null)
                {
                    OnRepetitionEnd(this, null);
                }
                mi = GetNextNew();
            }

            if (mi == null)
            {
                if (OnNewEnd != null)
                {
                    OnNewEnd(this, null);
                }

                mi = GetNextDrill();
            }

            if (mi == null)
            {
                if (OnDrillEnd != null)
                {
                    OnDrillEnd(this, null);
                }
            }

            return mi;
        }


        public MemoItem GetNextRepetition()
        {
            MemoItem ri = null;
            if (_RepeList.Count > 0 && _RepeCursor < _RepeList.Count)
            {
                long id = _RepeList[_RepeCursor].ItemID;

                var items = database.MemoItem.Where(c => c.ItemID == id);
                foreach (var item in items)
                {
                    ri = item;
                }
                _RepeCursor++;
            }


            return ri;
        }


        public MemoItem GetNextNew()
        {
            MemoItem ri = null;
            if (_NewList.Count > 0 && _NewCursor < _NewList.Count)
            {
                long id = _NewList[_NewCursor].ItemID;

                var items = database.MemoItem.Where(c => c.ItemID == id);

                foreach (var item in items)
                {
                    ri = item;
                }
                _NewCursor++;
            }

            return ri;
        }

        public MemoItem GetNextDrill()
        {
            MemoItem ri = null;

            if (_DrillList.Count > 0 && _DrillCursor >= _DrillList.Count)
            {
                _DrillCursor = 0;
            }

            if (_DrillList.Count > 0 && _DrillCursor < _DrillList.Count)
            {
                long id = _DrillList[_DrillCursor].ItemID;

                ri = database.MemoItem.Where(c => c.ItemID == id).FirstOrDefault();

                _DrillCursor++;
            }



            return ri;
        }

        public MemoItem GetStartItem()
        {
            return GetNextItem();
        }

        public int GetHistoryDoneByBook(long bookID)
        {
            //include inactivited
            var hisList = database.MemoHistory.Where(c => c.LoginID == LoginID && c.Book == this.BookID);
            return hisList.Count();
        }

        /// <summary>
        /// Get history from today history list
        /// </summary>
        /// <param name="itemID"></param>
        /// <returns></returns>
        public MemoHistory GetHistoryInfo(long itemID)
        {
            var hisList = _HistoryList.Where(c => c.ItemID == itemID && c.LoginID == LoginID && c.Book == this.BookID).ToList();
            if (hisList != null && hisList.Count > 0)
            {
                return hisList[0];
            }

            return null;
        }

        public void EstimateTodayList()
        {
            _RepeCursor = 0;
            _RepeList.Clear();

            _NewCursor = 0;
            _NewList.Clear();

            _DrillCursor = 0;
            _DrillList.Clear();

            _HistoryList.Clear();

            int totalNew = 0;
            int totalRepe = 0;
            int totalDrill = 0;

            DateTime dt = DateTime.Now.Date;
            var hisList = database.MemoHistory.Where(c => c.LoginID == LoginID && c.Book == this.BookID && c.LastDate < dt);

            foreach (var his in hisList)
            {
                if (his.LastResult > 0)
                {
                    if (his.CurveIndex > 0)
                    {
                        long minutes = Curve.GetNextMinutes(his.CurveIndex);
                        if (his.LastDate != null)
                        {
                            //DateTime showed = DateTime.Parse(his.ShowedDate);
                            DateTime plan = his.LastDate.Value.AddMinutes(minutes);
                            if (plan <= DateTime.Now)
                            {
                                _RepeList.Add(new MemoToday { ItemID = his.ItemID, TodayID = totalRepe, LoginID = this.LoginID });
                                totalRepe++;
                                _HistoryList.Add(his);
                            }
                        }
                        else
                        {
                            _RepeList.Add(new MemoToday { ItemID = his.ItemID, TodayID = totalRepe, LoginID = this.LoginID });
                            totalRepe++;
                            _HistoryList.Add(his);
                        }
                    }
                }
                else
                {
                    _RepeList.Add(new MemoToday { ItemID = his.ItemID, TodayID = totalRepe, LoginID = this.LoginID });
                    totalRepe++;
                    _HistoryList.Add(his);
                }

            }

            _RepetitionToday = totalRepe;

            //part 2. parepare to remember new word from item table.
            //ObjectSet<MemoItem> list = context.MemoItem.Where ( c=> c.;
            //var list = from ri in context.MemoItem
            //           join l in context.ItemCategoryLink on ri.ItemID equals l.ItemID 
            //           select ri;

            //var list = database.MemoItem.Where ( c=> c.Book == this.BookID ) ;

            if (NewItemMax < 1) NewItemMax = 10;

            int NewCheckedCount = 0;

            var newChecked = from mh in database.MemoHistory where mh.StartDate > dt && mh.LoginID == this.LoginID select mh.ItemID;
            if (newChecked != null)
            {
                NewCheckedCount = newChecked.Count();


                NewItemMax = NewItemMax - NewCheckedCount;

            }

            if (NewItemMax > 0)
            {
                var newlist = (from ri in database.MemoItem
                               where ri.Book == this.BookID
                               where !(from rh in database.MemoHistory where rh.LoginID == this.LoginID select rh.ItemID).Contains(ri.ItemID)
                               select ri).OrderBy(x => x.Category).ThenBy(x => x.ItemOrder).Take(NewItemMax);

                foreach (var item in newlist)
                {
                    _NewList.Add(new MemoToday { ItemID = item.ItemID, TodayID = totalNew, LoginID = this.LoginID });

                    totalNew++;

                }

                _NewToday = totalNew;
            }

            //part 3

            totalDrill = 0;
            var drills = GetDrillListFromDB();
            if (drills != null)
            {
                foreach (var item in drills)
                {
                    _DrillList.Add(new MemoToday { ItemID = item.ItemID, TodayID = totalDrill, LoginID = this.LoginID });
                    totalDrill++;
                }
            }

            _DrillToday = totalDrill;
        }

    }
}
